#!/usr/bin/env python3
# -*- coding: utf-8
"""
基于Weaviate的场景记忆服务
为每个场景创建唯一ID，将场景信息向量化存储到Weaviate，支持智能场景识别和记忆检索
"""

import hashlib
import json
import time
from typing import Dict, Any, List, Optional, Tuple
from datetime import datetime
import weaviate
from weaviate.classes.config import Configure, DataType
from weaviate.classes.init import Auth
from utils.logger import emobot_logger

logger = emobot_logger.get_logger()


class SceneMemoryService:
    """基于Weaviate的场景记忆服务"""

    def __init__(
        self,
        weaviate_url: str = "http://192.168.5.93:8080",
        openai_api_key: str = None,
        openai_base_url: str = None,
    ):
        """
        初始化Weaviate场景记忆服务

        Args:
            weaviate_url: Weaviate服务地址
            openai_api_key: OpenAI API密钥（用于embedding）
            openai_base_url: OpenAI基础URL（用于embedding）
        """
        self.openai_api_key = openai_api_key
        self.openai_base_url = openai_base_url

        try:
            # 连接到Weaviate - 使用标准的HTTP连接方式
            if "://" in weaviate_url:
                # 解析URL
                if weaviate_url.startswith("http://"):
                    host = weaviate_url.replace("http://", "").split(":")[0]
                    port = (
                        int(weaviate_url.split(":")[-1])
                        if ":" in weaviate_url
                        else 8080
                    )
                else:
                    host = weaviate_url.replace("https://", "").split(":")[0]
                    port = (
                        int(weaviate_url.split(":")[-1])
                        if ":" in weaviate_url
                        else 8080
                    )
            else:
                host = weaviate_url.split(":")[0]
                port = int(weaviate_url.split(":")[-1]) if ":" in weaviate_url else 8080

            # 使用标准连接方式，并传递OpenAI API密钥
            connection_kwargs = {
                "host": host,
                "port": port,
                "skip_init_checks": True,  # 跳过版本检查
            }

            # 如果有OpenAI配置，添加到headers中
            if self.openai_api_key:
                connection_kwargs["headers"] = {"X-OpenAI-Api-Key": self.openai_api_key}
                logger.info(
                    f"Connected to Weaviate at {host}:{port} with OpenAI API key"
                )
            else:
                logger.info(f"Connected to Weaviate at {host}:{port}")

            self.client = weaviate.connect_to_local(**connection_kwargs)

            # 确保场景集合存在
            self._ensure_scene_collection()

            logger.info("SceneMemoryService initialized successfully")

        except Exception as e:
            logger.error(f"Failed to initialize Weaviate client: {e}")
            raise

    def _ensure_scene_collection(self):
        """确保场景集合存在，如果不存在则创建"""
        try:
            # 检查集合是否存在
            if not self.client.collections.exists("Scene"):
                # 使用OpenAI向量化器配置，支持文字到向量的转换
                if self.openai_api_key and self.openai_base_url:
                    # 配置OpenAI向量化器
                    vectorizer_config = Configure.Vectorizer.text2vec_openai(
                        model="text-embedding-3-large", type_="text", dimensions=3072
                    )
                    logger.info("Using OpenAI text-embedding-3-large vectorizer")
                else:
                    # 如果没有OpenAI配置，使用none向量化器
                    vectorizer_config = None
                    logger.info("Using no vectorizer - manual vector mode")

                # 创建向量配置
                vector_config = Configure.VectorIndex.hnsw(distance="cosine")

                # 创建场景集合
                self.client.collections.create(
                    name="Scene",
                    properties=[
                        {
                            "name": "scene_id",
                            "data_type": DataType.TEXT,
                            "description": "场景唯一标识符",
                        },
                        {
                            "name": "scene_name",
                            "data_type": DataType.TEXT,
                            "description": "场景名称",
                        },
                        {
                            "name": "platform",
                            "data_type": DataType.TEXT,
                            "description": "平台位置",
                        },
                        {
                            "name": "items",
                            "data_type": DataType.TEXT_ARRAY,
                            "description": "物品列表",
                        },
                        {
                            "name": "scene_description",
                            "data_type": DataType.TEXT,
                            "description": "场景描述",
                        },
                        {
                            "name": "created_at",
                            "data_type": DataType.DATE,
                            "description": "创建时间",
                        },
                        {
                            "name": "updated_at",
                            "data_type": DataType.DATE,
                            "description": "更新时间",
                        },
                        {
                            "name": "visit_count",
                            "data_type": DataType.INT,
                            "description": "访问次数",
                        },
                    ],
                    vectorizer_config=vectorizer_config,
                    vector_index_config=vector_config,
                    description="场景记忆集合",
                )
                logger.info("Created Scene collection in Weaviate")
            else:
                logger.info("Scene collection already exists")

        except Exception as e:
            logger.error(f"Error ensuring scene collection: {e}")
            raise

    def generate_scene_id(self, scene: str, items: List[str]) -> str:
        """
        按顺序生成唯一场景ID

        Args:
            scene: 场景名称
            items: 物品列表

        Returns:
            str: 唯一的场景ID
        """
        try:
            collection = self.client.collections.get("Scene")

            # 获取当前场景总数
            response = collection.query.fetch_objects(
                limit=1000, return_properties=["scene_id"]  # 获取足够多的记录来统计
            )

            # 计算下一个ID
            next_id = len(response.objects) + 1
            scene_id = f"scene_{next_id:06d}"  # 使用6位数字，如scene_000001

            logger.info(f"Generated sequential scene ID: {scene_id} for scene: {scene}")
            return scene_id

        except Exception as e:
            logger.error(f"Error generating scene ID: {e}")
            # 如果出错，使用时间戳作为备用方案
            import time

            timestamp = int(time.time())
            scene_id = f"scene_{timestamp}"
            logger.info(f"Using timestamp-based scene ID: {scene_id}")
            return scene_id

    def _create_scene_vector(self, scene_data: Dict[str, Any]) -> str:
        """
        创建场景的向量化描述文本

        Args:
            scene_data: 场景数据

        Returns:
            str: 向量化描述文本
        """
        scene_name = scene_data.get("scene", "")
        items = scene_data.get("items", [])

        # 构建描述文本，用于向量化（去掉位置信息，提高匹配精度）
        description_parts = [f"场景类型: {scene_name}", f"物品: {', '.join(items)}"]

        # 添加场景特征描述
        if "会议室" in scene_name:
            description_parts.append("这是一个会议空间，通常用于团队讨论和演示")
        elif "办公室" in scene_name:
            description_parts.append("这是一个工作空间，通常用于个人办公和专注工作")
        elif "厨房" in scene_name:
            description_parts.append("这是一个烹饪空间，通常用于食物准备和烹饪")
        elif "客厅" in scene_name:
            description_parts.append("这是一个生活空间，通常用于休闲和家庭活动")

        return "。".join(description_parts)

    def store_scene_memory(
        self, scene_data: Dict[str, Any], user_id: str = None
    ) -> Dict[str, Any]:
        """
        存储场景记忆到Weaviate

        Args:
            scene_data: 场景数据
            user_id: 用户ID（可选）

        Returns:
            Dict: 存储结果
        """
        try:
            # 生成场景ID
            scene_id = self.generate_scene_id(scene_data["scene"], scene_data["items"])

            # 创建向量化描述
            scene_description = self._create_scene_vector(scene_data)

            # 准备存储数据 - 使用RFC3339格式的日期
            current_time = datetime.now()
            scene_object = {
                "scene_id": scene_id,
                "scene_name": scene_data["scene"],
                "items": scene_data["items"],
                "scene_description": scene_description,
                "created_at": current_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
                "updated_at": current_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
                "visit_count": 1,
            }

            # 存储到Weaviate
            collection = self.client.collections.get("Scene")
            result = collection.data.insert(scene_object)

            if result:
                logger.info(f"Successfully stored scene memory: {scene_id}")
                return {
                    "action": "created",
                    "scene_id": scene_id,
                    "scene": scene_data["scene"],
                    "items": scene_data["items"],
                    "similarity": 1.0,
                    "weaviate_id": str(result),
                    "message": "新场景已添加到记忆",
                }
            else:
                raise Exception("Failed to store scene in Weaviate")

        except Exception as e:
            logger.error(f"Error storing scene memory: {e}")
            raise

    def find_similar_scenes(
        self,
        scene_data: Dict[str, Any],
        similarity_threshold: float = 0.7,
        limit: int = 5,
    ) -> List[Dict[str, Any]]:
        """
        在Weaviate中查找相似场景（使用手动文本相似度计算）

        Args:
            scene_data: 新场景数据
            similarity_threshold: 相似度阈值
            limit: 返回结果数量限制

        Returns:
            List[Dict]: 相似场景列表
        """
        try:
            # 直接使用文本相似度搜索，不依赖向量化器
            return self._find_similar_scenes_by_text(
                scene_data, similarity_threshold, limit
            )

        except Exception as e:
            logger.error(f"Error finding similar scenes: {e}")
            return []

    def _find_similar_scenes_by_text(
        self,
        scene_data: Dict[str, Any],
        similarity_threshold: float = 0.7,
        limit: int = 5,
    ) -> List[Dict[str, Any]]:
        """
        基于文本相似度查找相似场景（不依赖向量化器）
        """
        try:
            collection = self.client.collections.get("Scene")

            # 获取所有场景进行文本相似度计算
            response = collection.query.fetch_objects(
                limit=1000,
                return_properties=[
                    "scene_id",
                    "scene_name",
                    "platform",
                    "items",
                    "created_at",
                    "updated_at",
                    "visit_count",
                ],
            )

            similar_scenes = []
            new_scene_text = self._create_scene_vector(scene_data).lower()

            for obj in response.objects:
                existing_scene_text = self._create_scene_vector(
                    {
                        "scene": obj.properties.get("scene_name", ""),
                        "items": obj.properties.get("items", []),
                    }
                ).lower()

                # 计算文本相似度（简单的词汇重叠度）
                similarity = self._calculate_text_similarity(
                    new_scene_text, existing_scene_text
                )

                if similarity >= similarity_threshold:
                    similar_scenes.append(
                        {
                            "scene_id": obj.properties["scene_id"],
                            "scene": obj.properties["scene_name"],
                            "items": obj.properties["items"],
                            "similarity": similarity,
                            "weaviate_id": str(obj.uuid),
                            "created_at": obj.properties.get("created_at", ""),
                            "updated_at": obj.properties.get("updated_at", ""),
                            "visit_count": obj.properties.get("visit_count", 1),
                        }
                    )

            # 按相似度降序排列
            similar_scenes.sort(key=lambda x: x["similarity"], reverse=True)

            logger.info(
                f"Found {len(similar_scenes)} similar scenes using text-based search"
            )
            return similar_scenes[:limit]

        except Exception as e:
            logger.error(f"Error in text-based search: {e}")
            return []

    def _calculate_text_similarity(self, text1: str, text2: str) -> float:
        """
        计算两个文本的相似度（基于词汇重叠和场景特征匹配）
        """
        try:
            # 分词
            words1 = set(text1.split())
            words2 = set(text2.split())

            if not words1 or not words2:
                return 0.0

            # 计算基础Jaccard相似度
            intersection = len(words1.intersection(words2))
            union = len(words1.union(words2))
            base_similarity = intersection / union if union > 0 else 0.0

            # 场景特征权重匹配
            scene_boost = 0.0

            # 检查场景类型匹配
            scene_keywords = ["会议室", "办公室", "厨房", "客厅", "卧室", "未知场景"]
            for keyword in scene_keywords:
                if keyword in text1 and keyword in text2:
                    scene_boost += 0.3
                    break

            # 检查物品匹配
            item_keywords = ["会议桌", "椅子", "笔记本电脑", "投影仪", "白板", "显示器", "未知物品"]
            item_matches = 0
            for keyword in item_keywords:
                if keyword in text1 and keyword in text2:
                    item_matches += 1

            if item_matches > 0:
                scene_boost += min(0.4, item_matches * 0.1)  # 最多0.4的加成

            # 组合相似度
            final_similarity = min(1.0, base_similarity + scene_boost)

            return final_similarity

        except Exception as e:
            logger.error(f"Error calculating text similarity: {e}")
            return 0.0

    def is_scene_remembered(
        self, scene_data: Dict[str, Any], similarity_threshold: float = 0.6
    ) -> Tuple[bool, Optional[Dict[str, Any]]]:
        """
        判断场景是否已经被记忆过

        Args:
            scene_data: 新场景数据
            similarity_threshold: 相似度阈值

        Returns:
            Tuple[bool, Optional[Dict]]: (是否记忆过, 最相似的场景数据)
        """
        try:
            similar_scenes = self.find_similar_scenes(
                scene_data, similarity_threshold, limit=1
            )

            if similar_scenes:
                most_similar = similar_scenes[0]
                logger.info(
                    f"Scene already remembered with similarity: {most_similar['similarity']:.3f}"
                )
                return True, most_similar
            else:
                logger.info("Scene not found in memory")
                return False, None

        except Exception as e:
            logger.error(f"Error checking if scene is remembered: {e}")
            return False, None

    def update_scene_memory(
        self, scene_id: str, new_items: List[str] = None, increment_visit: bool = True
    ) -> bool:
        """
        更新场景记忆

        Args:
            scene_id: 场景ID
            new_items: 新的物品列表
            increment_visit: 是否增加访问次数

        Returns:
            bool: 更新是否成功
        """
        try:
            collection = self.client.collections.get("Scene")

            # 查找现有场景
            response = collection.query.fetch_objects(
                where={
                    "path": ["scene_id"],
                    "operator": "Equal",
                    "valueString": scene_id,
                },
                limit=1,
            )

            if not response.objects:
                logger.warning(f"Scene not found: {scene_id}")
                return False

            scene_obj = response.objects[0]
            weaviate_id = scene_obj.uuid

            # 准备更新数据 - 使用RFC3339格式的日期
            update_data = {"updated_at": datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")}

            if new_items:
                # 合并物品列表
                existing_items = scene_obj.properties.get("items", [])
                combined_items = list(set(existing_items + new_items))
                update_data["items"] = combined_items

            if increment_visit:
                current_visit_count = scene_obj.properties.get("visit_count", 0)
                update_data["visit_count"] = current_visit_count + 1

            # 更新场景
            collection.data.update(uuid=weaviate_id, properties=update_data)

            logger.info(f"Successfully updated scene memory: {scene_id}")
            return True

        except Exception as e:
            logger.error(f"Error updating scene memory: {e}")
            return False

    def create_or_update_scene_memory(
        self, scene_data: Dict[str, Any], user_id: str = None
    ) -> Dict[str, Any]:
        """
        创建或更新场景记忆

        Args:
            scene_data: 场景数据
            user_id: 用户ID（可选）

        Returns:
            Dict: 操作结果
        """
        try:
            # 检查是否已存在相似场景
            is_remembered, similar_scene = self.is_scene_remembered(scene_data)

            if is_remembered:
                # 更新现有场景
                new_items = scene_data.get("items", [])
                update_success = self.update_scene_memory(
                    similar_scene["scene_id"], new_items=new_items, increment_visit=True
                )

                if update_success:
                    return {
                        "action": "updated",
                        "scene_id": similar_scene["scene_id"],
                        "scene": similar_scene["scene"],
                        "items": similar_scene["items"],
                        "similarity": similar_scene["similarity"],
                        "weaviate_id": similar_scene["weaviate_id"],
                        "message": f"场景已记忆过，相似度: {similar_scene['similarity']:.3f}，已更新物品列表",
                    }

            # 创建新场景记忆
            return self.store_scene_memory(scene_data, user_id)

        except Exception as e:
            logger.error(f"Error creating or updating scene memory: {e}")
            raise

    def search_scene_memory(
        self, query: str, search_type: str = "all", limit: int = 10
    ) -> List[Dict[str, Any]]:
        """
        搜索场景记忆

        Args:
            query: 搜索查询
            search_type: 搜索类型 ("scene", "items", "all") - 去掉platform类型
            limit: 返回结果数量限制

        Returns:
            List[Dict]: 搜索结果
        """
        try:
            collection = self.client.collections.get("Scene")

            # 构建搜索查询（去掉位置相关搜索）
            if search_type == "scene":
                search_text = f"场景类型: {query}"
            elif search_type == "items":
                search_text = f"物品: {query}"
            else:
                search_text = query

            # 首先尝试向量搜索
            try:
                response = collection.query.near_text(
                    query=search_text,
                    limit=limit,
                    return_properties=[
                        "scene_id",
                        "scene_name",
                        "platform",
                        "items",
                        "created_at",
                        "updated_at",
                        "visit_count",
                    ],
                )

                results = []
                for obj in response.objects:
                    distance = obj.metadata.distance
                    similarity = max(0, 1 - distance)

                    results.append(
                        {
                            "scene_id": obj.properties["scene_id"],
                            "scene": obj.properties["scene_name"],
                            "items": obj.properties["items"],
                            "similarity": similarity,
                            "weaviate_id": str(obj.uuid),
                            "match_type": search_type,
                            "created_at": obj.properties.get("created_at", ""),
                            "visit_count": obj.properties.get("visit_count", 1),
                        }
                    )

                # 按相似度排序
                results.sort(key=lambda x: x["similarity"], reverse=True)
                logger.info(
                    f"Scene memory search returned {len(results)} results using vector search for query: {query}"
                )
                return results

            except Exception as e:
                logger.warning(
                    f"Vector search failed: {e}, falling back to text-based search"
                )

                # 回退到文本搜索
                return self._search_scene_memory_by_text(query, search_type, limit)

        except Exception as e:
            logger.error(f"Error searching scene memory: {e}")
            return []

    def _search_scene_memory_by_text(
        self, query: str, search_type: str = "all", limit: int = 10
    ) -> List[Dict[str, Any]]:
        """
        基于文本搜索场景记忆（不依赖向量化器）
        """
        try:
            collection = self.client.collections.get("Scene")

            # 获取所有场景进行文本匹配
            response = collection.query.fetch_objects(
                limit=1000,
                return_properties=[
                    "scene_id",
                    "scene_name",
                    "platform",
                    "items",
                    "created_at",
                    "updated_at",
                    "visit_count",
                ],
            )

            results = []
            query_lower = query.lower()

            for obj in response.objects:
                scene_name = obj.properties.get("scene_name", "").lower()
                items = [item.lower() for item in obj.properties.get("items", [])]

                # 计算文本匹配度
                similarity = 0.0
                if search_type == "scene" and query_lower in scene_name:
                    similarity = 0.8
                elif search_type == "items" and any(
                    query_lower in item for item in items
                ):
                    similarity = 0.7
                elif search_type == "all":
                    # 综合匹配度
                    if query_lower in scene_name:
                        similarity += 0.6
                    if any(query_lower in item for item in items):
                        similarity += 0.4

                if similarity > 0:
                    results.append(
                        {
                            "scene_id": obj.properties["scene_id"],
                            "scene": obj.properties["scene_name"],
                            "items": obj.properties["items"],
                            "similarity": similarity,
                            "weaviate_id": str(obj.uuid),
                            "match_type": search_type,
                            "created_at": obj.properties.get("created_at", ""),
                            "visit_count": obj.properties.get("visit_count", 1),
                        }
                    )

            # 按相似度排序
            results.sort(key=lambda x: x["similarity"], reverse=True)
            logger.info(
                f"Scene memory search returned {len(results)} results using text search for query: {query}"
            )
            return results[:limit]

        except Exception as e:
            logger.error(f"Error in text-based scene search: {e}")
            return []

    def get_scene_memory_summary(self) -> Dict[str, Any]:
        """
        获取场景记忆摘要信息

        Returns:
            Dict: 场景记忆摘要
        """
        try:
            collection = self.client.collections.get("Scene")

            # 获取所有场景
            response = collection.query.fetch_objects(
                limit=1000,  # 获取足够多的场景进行统计
                return_properties=["scene_name", "platform", "items", "visit_count"],
            )

            # 统计信息
            total_scenes = len(response.objects)
            scene_types = {}
            common_items = {}
            total_visits = 0

            for obj in response.objects:
                # 场景类型统计
                scene_name = obj.properties.get("scene_name", "")
                scene_types[scene_name] = scene_types.get(scene_name, 0) + 1

                # 常见物品统计
                items = obj.properties.get("items", [])
                for item in items:
                    common_items[item] = common_items.get(item, 0) + 1

                # 访问次数统计
                visit_count = obj.properties.get("visit_count", 1)
                total_visits += visit_count

            # 排序
            scene_types = dict(
                sorted(scene_types.items(), key=lambda x: x[1], reverse=True)
            )
            common_items = dict(
                sorted(common_items.items(), key=lambda x: x[1], reverse=True)
            )

            return {
                "total_scenes": total_scenes,
                "total_visits": total_visits,
                "scene_types": scene_types,
                "top_items": dict(list(common_items.items())[:10]),  # 前10个常见物品
                "last_updated": datetime.now().isoformat(),
            }

        except Exception as e:
            logger.error(f"Error getting scene memory summary: {e}")
            return {}

    def delete_scene_memory(self, scene_id: str) -> bool:
        """
        删除场景记忆

        Args:
            scene_id: 场景ID

        Returns:
            bool: 删除是否成功
        """
        try:
            collection = self.client.collections.get("Scene")

            # 查找场景
            response = collection.query.fetch_objects(
                where={
                    "path": ["scene_id"],
                    "operator": "Equal",
                    "valueString": scene_id,
                },
                limit=1,
            )

            if not response.objects:
                logger.warning(f"Scene not found for deletion: {scene_id}")
                return False

            # 删除场景 - 使用正确的API
            weaviate_id = response.objects[0].uuid
            try:
                # 尝试使用新的API
                collection.data.delete_by_id(weaviate_id)
            except AttributeError:
                try:
                    # 尝试使用旧的API
                    collection.data.delete(weaviate_id)
                except AttributeError:
                    # 尝试使用另一种方法
                    collection.delete(weaviate_id)

            logger.info(f"Successfully deleted scene memory: {scene_id}")
            return True

        except Exception as e:
            logger.error(f"Error deleting scene memory: {e}")
            return False

    def close(self):
        """关闭Weaviate连接"""
        try:
            if hasattr(self, "client"):
                self.client.close()
                logger.info("Weaviate connection closed")
        except Exception as e:
            logger.error(f"Error closing Weaviate connection: {e}")
